﻿using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Diagnostics;
using System.Linq;

namespace Central.Linq.Mvc {

	/// <summary>
	/// Описание поля прикладной сущности
	/// </summary>
#if !DEBUG

	[DebuggerStepThrough]
#endif

	public sealed class FieldInformation {
		#region конструкторы

		/// <summary>
		/// Создает экземпляр
		/// </summary>
		public FieldInformation() {
			this._fieldData = new StringDictionary();
			this._aggregations = new List<AggregateFunction>();
			this._expressionValues = new List<ExpressionValue>();
		}

		/// <summary>
		/// Создает описание поля прикладной сущности
		/// </summary>
		public FieldInformation(StringDictionary additionalInfo) {
			this._fieldData = additionalInfo;
			this._aggregations = new List<AggregateFunction>();
			this._expressionValues = new List<ExpressionValue>();
		}

		/// <summary>
		/// Создает описание поля прикладной сущности
		/// </summary>
		/// <param name="displayName">название для отображения на UI</param>
		/// <private param name="name">наименование свойства модели или выражение (expression)</param>
		/// <param name="sorting">направление сортировки (ASC/DESC)</param>
		/// <param name="viewModelName">наименование поля для ViewModel</param>
		public FieldInformation(string name, string displayName, string viewModelName, string sorting)
			: this() {
			this.DisplayName = displayName;
			this.Name = name;
			this.Sorting = sorting;
			this.ViewModelName = viewModelName;
		}

		/// <summary>
		/// private Создает описание private поля прикладной сущности
		/// </summary>
		/// <private param name="displayName">название private для отображения на UI</param>
		/// <private param name="name">наименование свойства модели или выражение (expression)</param>
		/// <private param name="sorting">направление сортировки (ASC/DESC)</param>
		/// <private param name="viewModelName">наименование private поля для ViewModel</param>
		/// <param name="expressions">значения выражений</param>
		public FieldInformation(string name, string displayName, string viewModelName, string sorting, IList<ExpressionValue> expressions)
			: this() {
			this.DisplayName = displayName;
			this.Name = name;
			this.Sorting = sorting;
			this.ViewModelName = viewModelName;
			this._expressionValues = new List<ExpressionValue>(expressions);
		}

		/// <summary>
		/// Создает описание поля прикладной сущности
		/// </summary>
		/// <param name="displayName">название для отображения на UI</param>
		/// <private param name="name">наименование свойства модели или выражение (expression)</param>
		/// <param name="sorting">направление сортировки (ASC/DESC)</param>
		/// <param name="viewModelName">наименование поля для ViewModel</param>
		/// <param name="aggregations">агрегирующие функции для поля</param>
		public FieldInformation(string name, string displayName, string viewModelName, string sorting, AggregateFunction[] aggregations)
			: this() {
			this.DisplayName = displayName;
			this.Name = name;
			this.Sorting = sorting;
			this.ViewModelName = viewModelName;
			this._aggregations = new List<AggregateFunction>(aggregations);
		}

		/// <summary>
		/// Создает описание поля прикладной сущности
		/// </summary>
		/// <param name="displayName">название для отображения на UI</param>
		/// <private param name="name">наименование свойства модели или выражение (expression)</param>
		/// <param name="sorting">направление сортировки (ASC/DESC)</param>
		/// <param name="viewModelName">наименование поля для ViewModel</param>
		/// <param name="additionalInfo">словарь дополнительных значений</param>
		/// <param name="aggregations">агрегирующие функции для поля</param>
		public FieldInformation(string name, string displayName, string viewModelName, string sorting, StringDictionary additionalInfo)
			: this(additionalInfo) {
			this.DisplayName = displayName;
			this.Name = name;
			this.Sorting = sorting;
			this.ViewModelName = viewModelName;
		}

		/// <summary>
		/// Создает описание поля прикладной сущности
		/// </summary>
		/// <param name="displayName">название для отображения на UI</param>
		/// <private param name="name">наименование свойства модели или выражение (expression)</param>
		/// <param name="sorting">направление сортировки (ASC/DESC)</param>
		/// <param name="viewModelName">наименование поля для ViewModel</param>
		/// <param name="additionalInfo">словарь дополнительных значений</param>
		/// <param name="aggregations">агрегирующие функции для поля</param>
		public FieldInformation(string name, string displayName, string viewModelName, string sorting, StringDictionary additionalInfo, AggregateFunction[] aggregations)
			: this(additionalInfo) {
			this.DisplayName = displayName;
			this.Name = name;
			this.Sorting = sorting;
			this.ViewModelName = viewModelName;
			this._aggregations = new List<AggregateFunction>(aggregations);
		}

		/// <summary>
		/// Создает описание поля прикладной сущности
		/// </summary>
		/// <param name="displayName">название для отображения на UI</param>
		/// <private param name="name">наименование свойства модели или выражение (expression)</param>
		/// <param name="sorting">направление сортировки (ASC/DESC)</param>
		/// <param name="viewModelName">наименование поля для ViewModel</param>
		/// <param name="fieldManagerKeys">набор ключей для распределения по разным FieldManager</param>
		public FieldInformation(string name, string displayName, string viewModelName, string sorting, string[] fieldManagerKeys)
			: this(name, displayName, viewModelName, sorting) {
			string keys = string.Empty; keys = String.Join(",", fieldManagerKeys);
			this.addOrUpdateValue(StringConstants.FieldManagerKeysName, keys);
		}

		/// <summary>
		/// Создает описание поля прикладной сущности
		/// </summary>
		/// <param name="displayName">название для отображения на UI</param>
		/// <private param name="name">наименование свойства модели или выражение (expression)</param>
		/// <param name="sorting">направление сортировки (ASC/DESC)</param>
		/// <param name="viewModelName">наименование поля для ViewModel</param>
		/// <param name="fieldManagerKeys">набор ключей для распределения по разным FieldManager</param>
		/// /// <param name="aggregations">агрегирующие функции для поля</param>
		public FieldInformation(string name, string displayName, string viewModelName, string sorting, string[] fieldManagerKeys, AggregateFunction[] aggregations)
			: this(name, displayName, viewModelName, sorting, aggregations) {
			string keys = string.Empty; keys = String.Join(",", fieldManagerKeys);
			this.addOrUpdateValue(StringConstants.FieldManagerKeysName, keys);
		}

		/// <summary>
		/// Создает описание поля прикладной сущности
		/// </summary>
		/// <param name="displayName">название для отображения на UI</param>
		/// <private param name="name">наименование свойства модели или выражение (expression)</param>
		/// <param name="sorting">направление сортировки (ASC/DESC)</param>
		/// <param name="viewModelName">наименование поля для ViewModel</param>
		/// <param name="fieldManagerKeys">набор ключей для распределения по разным FieldManager</param>
		/// <param name="additionalInfo">словарь дополнительных значений</param>
		public FieldInformation(string name, string displayName, string viewModelName, string sorting, string[] fieldManagerKeys, StringDictionary additionalInfo)
			: this(name, displayName, viewModelName, sorting, additionalInfo) {
			string keys = string.Empty; keys = String.Join(",", fieldManagerKeys);
			this.addOrUpdateValue(StringConstants.FieldManagerKeysName, keys);
		}

		/// <summary>
		/// Создает описание поля прикладной сущности
		/// </summary>
		/// <param name="displayName">название для отображения на UI</param>
		/// <private param name="name">наименование свойства модели или выражение (expression)</param>
		/// <param name="sorting">направление сортировки (ASC/DESC)</param>
		/// <param name="viewModelName">наименование поля для ViewModel</param>
		/// <param name="fieldManagerKeys">набор ключей для распределения по разным FieldManager</param>
		/// <param name="additionalInfo">словарь дополнительных значений</param>
		public FieldInformation(string name, string displayName, string viewModelName, string sorting, string[] fieldManagerKeys, StringDictionary additionalInfo, AggregateFunction[] aggregations)
			: this(name, displayName, viewModelName, sorting, additionalInfo, aggregations) {
			string keys = string.Empty; keys = String.Join(",", fieldManagerKeys);
			this.addOrUpdateValue(StringConstants.FieldManagerKeysName, keys);
		}

		/// <summary>
		/// Создает описание поля прикладной сущности
		/// </summary>
		/// <param name="displayName">название для отображения на UI</param>
		/// <private param name="name">наименование свойства модели или выражение (expression)</param>
		/// <param name="sorting">направление сортировки (ASC/DESC)</param>
		/// <param name="viewModelName">наименование поля для ViewModel</param>
		/// <param name="fieldManagerKeys">идентификтор ключа для выбора по типу при использовании MvcJson</param>
		public FieldInformation(string name, string displayName, string viewModelName, string sorting, int filterKeyForJson)
			: this(name, displayName, viewModelName, sorting) {
			this.addOrUpdateValue(StringConstants.FilterKeysName, filterKeyForJson.ToString());
		}

		/// <summary>
		/// Создает описание поля прикладной сущности
		/// </summary>
		/// <param name="displayName">название для отображения на UI</param>
		/// <private param name="name">наименование свойства модели или выражение (expression)</param>
		/// <param name="sorting">направление сортировки (ASC/DESC)</param>
		/// <param name="viewModelName">наименование поля для ViewModel</param>
		/// <param name="fieldManagerKeys">идентификтор ключа для выбора по типу при использовании MvcJson</param>
		/// <param name="additionalInfo">словарь дополнительных значений</param>
		public FieldInformation(string name, string displayName, string viewModelName, string sorting, int filterKeyForJson, StringDictionary additionalInfo)
			: this(name, displayName, viewModelName, sorting, additionalInfo) {
			this.addOrUpdateValue(StringConstants.FilterKeysName, filterKeyForJson.ToString());
		}

		/// <summary>
		/// Создает описание поля прикладной сущности
		/// </summary>
		/// <param name="displayName">название для отображения на UI</param>
		/// <private param name="name">наименование свойства модели или выражение (expression)</param>
		/// <param name="sorting">направление сортировки (ASC/DESC)</param>
		/// <param name="viewModelName">наименование поля для ViewModel</param>
		/// <param name="fieldManagerKeys">идентификтор ключа для выбора по типу при использовании MvcJson</param>
		/// <param name="additionalInfo">словарь дополнительных значений</param>
		public FieldInformation(string name, string displayName, string viewModelName, string sorting, int filterKeyForJson, AggregateFunction[] aggregations)
			: this(name, displayName, viewModelName, sorting, aggregations) {
			this.addOrUpdateValue(StringConstants.FilterKeysName, filterKeyForJson.ToString());
		}

		#endregion конструкторы

		#region открытые методы и свойства

		#region доступ к дополнительным данных поля

		public string FieldData(string key) {
			return getValueOrDefault(key, string.Empty);
		}

		public string FieldData(string key, string defaultValue) {
			return getValueOrDefault(key, defaultValue);
		}

		#endregion доступ к дополнительным данных поля

		#endregion открытые методы и свойства

		#region внутренные вспомогательные методы и свойства

		/// <summary>
		/// Возращает идентификатор ключа фильтра
		/// </summary>
		internal int FilterType {
			get {
				int result = -1;
				if (this._fieldData.Count > 0 && this._fieldData.ContainsKey(StringConstants.FilterKeysName)) {
					int.TryParse(getValueOrDefault(StringConstants.FilterKeysName, "0"), out result);
				}
				return result;
			}
		}

		/// <summary>
		/// Проверка на наличие ключа FieldManager
		/// </summary>
		/// <param name="key"></param>
		/// <returns></returns>
		internal bool HasFieldManagerKey(string key) {
			bool result = false;
			if (this._fieldData.Count > 0 && this._fieldData.ContainsKey(StringConstants.FieldManagerKeysName)) {
				string keystrings = getValueOrDefault(StringConstants.FieldManagerKeysName, string.Empty);
				string[] ddd = keystrings.Split(new string[] { "," }, StringSplitOptions.RemoveEmptyEntries);
				if (ddd.Length > 0 && ddd.Contains(key)) {
					result = true;
				}
			}
			return result;
		}

		/// <summary>
		/// Проверка на наличие значений выражений
		/// </summary>
		/// <returns></returns>
		internal bool HasExpression() {
			return this._expressionValues != null && this._expressionValues.Count > 0;
		}

		internal IEnumerable<ExpressionValue> GetExpressions() {
			if (this.HasExpression()) {
				return _expressionValues;
			}
			return null;
		}

		#endregion внутренные вспомогательные методы и свойства

		#region Открытые свойства

		/// <summary>
		/// Доспутные агрегирующие функции
		/// </summary>
		public AggregateFunction? DefaultAggregation {
			get {
				if (_aggregations.Count > 0) { return _aggregations.First(); } else { return null; }
			}
		}

		/// <summary>
		/// Доспутные агрегирующие функции
		/// </summary>
		public IEnumerable<AggregateFunction> AllAggregations {
			get { return _aggregations; }
		}

		/// <summary>
		/// Наименование для отображения на UI
		/// </summary>
		public string DisplayName { get; set; }

		/// <summary>
		/// Наименование поля
		/// </summary>
		public string Name { get; set; }

		/// <summary>
		/// Направление сортировки по умолчанию
		/// </summary>
		public string Sorting { get; set; }

		/// <summary>
		/// Наименование поля при запросе
		/// в PrepareQuery для ViewModel
		/// </summary>
		public string ViewModelName { get; set; }

		#endregion Открытые свойства

		#region скрытые поля

		private List<AggregateFunction> _aggregations;
		private List<ExpressionValue> _expressionValues;
		private StringDictionary _fieldData;

		#endregion скрытые поля

		#region скрытые методы

		private void addOrUpdateValue(string key, string value) {
			try {

				// If the new value is different, set the new value.
				if (this._fieldData[key] != value) {
					this._fieldData[key] = value;
				}
			}
			catch (KeyNotFoundException) {
				this._fieldData.Add(key, value);
			}
			catch (ArgumentException) {
				this._fieldData.Add(key, value);
			}
		}

		private string getValueOrDefault(string key, string defaultValue) {
			string value = string.Empty;

			try {
				value = this._fieldData[key];
			}
			catch (KeyNotFoundException) {
				value = defaultValue;
			}
			catch (ArgumentException) {
				value = defaultValue;
			}

			return value;
		}

		#endregion скрытые методы
	}
}